package client.neo4j;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableTable;
import entities.PlainAddable;
import org.neo4j.driver.v1.Session;
import org.neo4j.driver.v1.StatementResult;
import relationships.*;
import utils.Neo4jConnectionUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class RelationshipOps {

    public static void addInvestRelation(Session session, String startEnterpriseID, String endEnterpriseID, InvestRelationship relationship) {

        String sql = String.format(" MATCH (e1:Enterprise), (e2:Enterprise) "
                + " WHERE e1.enterpriseID = '%s' and e2.enterpriseID = '%s' "
                + " CREATE (e1) -[r: %s]-> (e2) ",
                startEnterpriseID, endEnterpriseID, relationship.toString());
        session.run(sql);

    }

    public static void addInvestApoc(Session session, List<Map<String, Object>> relations) {
        Map<String, Object> params = new HashMap<>();
        params.put("relations", relations);
        String sql = " UNWIND $relations as relation " +
                " MATCH (e1:Enterprise) WHERE e1.enterpriseID = relation.start " +
                " MATCH (e2:Enterprise) WHERE e2.enterpriseID = relation.end " +
                " CALL apoc.create.relationship(e1,relation.rel_type,relation.rel_props, e2) yield rel " +
                " RETURN count(*)";
        session.run(sql, params);
    }

    public static void batchAddInvestApoc(Session session, List<Map<String, Object>> relations){
        Map<String, Object> params = new HashMap<>();
        params.put("relations", relations);


    }

    public static void addRepresentativeRelation(Session session, String personName, String enterpriseID, RepreRelationship relationship){

        String sql = String.format(" MATCH (p:Person), (e:Enterprise) "
                + " WHERE p.name = '%s' and e.enterpriseID = '%s' "
                + " CREATE (p) - [r: %s]-> (e) ",
                personName, enterpriseID, relationship);
        session.run(sql);

    }

    public static void addMemberRelation(Session session, String personName, String enterpriseID, MemberRelationship relationship){

        String sql = String.format(" MATCH (p:Person), (e:Enterprise) "
                        + " WHERE p.name = '%s' and e.enterpriseID = '%s' "
                        + " CREATE (p) - [r: %s]-> (e) ",
                personName, enterpriseID, relationship);
        session.run(sql);

    }

    public static void addShareholderRelation(Session session, String investorName, String enterpriseID, ShareholderRelationship relationship){

        String sql = String.format(" MATCH (i), (e:Enterprise) "
                        + " WHERE (i.name = '%1$s' or i.enterpriseName = '%1$s') AND e.enterpriseID = '%2$s' "
                        + " CREATE (i) -[r: %3$s]-> (e) ",
                investorName, enterpriseID, relationship);
        session.run(sql);

    }

    public static void addFloatShareholderRelation(Session session, String name, String stockCode, FloatShareRelationship relationship){

        String sql = String.format(" MATCH (i), (e:Enterprise) "
                        + " WHERE (i.name = '%1$s' or i.enterpriseName = '%1$s') AND e.stock_code = '%2$s' "
                        + " CREATE (i) -[r: %3$s]-> (e) ",
                name, stockCode, relationship);
        session.run(sql);

    }

    public static void addMajorShareholderRelation(Session session, String name, String stockCode, MajorShareRelationship relationship){

        String sql = String.format(" MATCH (i), (e:Enterprise) "
                        + " WHERE (i.name = '%1$s' or i.enterpriseName = '%1$s') AND e.stock_code = '%2$s' "
                        + " CREATE (i) -[r: %3$s]-> (e) ",
                name, stockCode, relationship);
        session.run(sql);

    }

    public static void addAddressRelation(Session session, String enterpriseID, String locName, AddressRelationship relationship){

        String sql = String.format(" MATCH (e:Enterprise), (l:Location) "
                        + " WHERE e.enterpriseID = '%s' AND l.locationName = '%s' "
                        + " CREATE (e) - [r: %s]-> (l) ",
                enterpriseID, locName, relationship);
        session.run(sql);

    }

    public static void addPlainRelation(Session session, String startType, String startQualifier, String startValue,
                                        String endType, String endQualifier, String endValue,
                                        PlainAddable relationship) {

        String sql = String.format(" MATCH (e1:%1$s), (e2:%4$s) " +
                " WHERE e1.%2$s = '%3$s' AND e2.%5$s = '%6$s' " +
                " CREATE (e1) -[:%7$s]-> (e2) ",
                startType, startQualifier, startValue, endType, endQualifier, endValue, relationship);
        session.run(sql);

    }

    public static StatementResult getRandomRelations(Session session, int limit) {
        String sql = " MATCH r = ()-[]-() WHERE rand() < 0.20 " +
                " WITH r LIMIT " + limit +
                " RETURN r ";
        return session.run(sql);
    }

    public static void addLawRelations(Session session, List<String> lawList, String id){
        for(String lawStr: lawList){
            String[] arr = lawStr.split("_");
            if(arr.length == 2){
              String sql = " MERGE (law1:Law{name:'" + arr[0] + "'}) \n"
                      + " MERGE (law2:Law{name:'" + arr[0] + arr[1] +"'}) \n"
                      + " WITH law1, law2 MATCH (j:Judgement{id:'"+ id +"'}) \n"
                      + " MERGE (law2) -[:BELONGS]-> (law1) \n"
                      + " MERGE (j)-[:BASED_ON]-> (law2) ";
              session.run(sql);
            } else System.out.println("malformed law string !");
        }

    }

    // update judgement id to doc_id
    public static void constructLawGraph(Session session, List<Map<String, Object>> props) {
        Map<String, Object> params = new HashMap<>();
        params.put("props", props);
        String sql = " UNWIND $props AS prop \n" +
                "MATCH (j:Judgement {doc_id: prop.doc_id}) \n" +
                "UNWIND prop.lawList AS lawStr \n" +
                "WITH j, split(lawStr, '_') AS lawArr \n" +
                "MERGE (law1:Law{name:lawArr[0]}) SET law1.level = 1 \n" +
                "MERGE (law2:Law{name:lawArr[0]+lawArr[1]}) SET law2.level = 2 \n" +
                "WITH j,law1,law2 \n" +
                "MERGE (law2) -[:BELONGS]-> (law1) \n" +
                "MERGE (j)-[:BASED_ON]-> (law2) ";
        session.run(sql, params);
    }

    // construct Judgement [:BASED_ON] new Law Graph
    public static void constructLawGraph2(Session session, List<Map<String, Object>> props){
        Map<String, Object> params = new HashMap<>();
        params.put("props", props);
        String sql = " UNWIND $props AS prop " +
                "MATCH (j:Judgement {doc_id: prop.doc_id}) " +
                "UNWIND prop.lawList AS lawStr " +
                "WITH j, split(lawStr, '_') AS lawArr " +
                "MATCH (law:Law{name:lawArr[0]+lawArr[1]}) " +
                "MERGE (j)-[:BASED_ON]-> (law) ";
        session.run(sql, params);
    }

    public static void constructJudgeGraph(Session session, List<Map<String, Object>> props) {
        Map<String, Object> params = new HashMap<>();
        params.put("props", props);
        String sql = " UNWIND $props AS prop " +
                " MATCH (j:Judgement {doc_id: prop.doc_id}) " +
                " MERGE (p:Judge {name: prop.judge_person}) " +
                " WITH j,p, prop.judge_type as judge_type " +
                " MERGE (j) -[:JUDGED_BY{position: judge_type}]-> (p) ";
        session.run(sql, params);
    }

    public static void constructCourtGraph(Session session, List<Map<String, Object>> props){
        Map<String, Object> params = new HashMap<>();
        params.put("props", props);
        String sql = " UNWIND $props AS prop " +
                " MATCH (c1:Court{court_id: prop.court_id}), (c2:Court{court_id: prop.pre_id}) " +
                " MERGE (c1) -[:SUBJECT_TO]-> (c2)";
        session.run(sql, params);
    }

    public static void constructCaseGraph(Session session, List<Map<String, Object>> props){
        Map<String, Object> params = new HashMap<>();
        params.put("props", props);
        String sql = " UNWIND $props AS prop " +
                " MATCH (c1:Case{case_id: prop.case_id}), (c2:Case{case_id: prop.pre_id}) " +
                " MERGE (c1) -[:BELONGS]-> (c2)";
        session.run(sql, params);
    }

    public static void constructJudgementRelations(Session session, List<Map<String, Object>> props) {
        Map<String, Object> params = new HashMap<>();
        params.put("props", props);
        String sql = " UNWIND $props AS prop " +
                " MATCH (c1:Judgement{doc_id: prop.doc_id}), (c2:Case{case_id: prop.case_id}), (c3:Court{court_id: prop.court_id}) " +
                " MERGE (c1) -[:RELATE_TO]-> (c2)" +
                " MERGE (c1) -[:LOCATE_IN]-> (c3) ";
        session.run(sql, params);
    }

    public static void constructJudgementRelations2(Session session, List<Map<String, Object>> props) {
        Map<String, Object> params = new HashMap<>();
        params.put("props", props);
        String sql = " UNWIND $props AS prop " +
                " MATCH (c1:Judgement{doc_id: prop.doc_id}), (c2:Court{court_name: prop.court_name}) " +
                " MERGE (c1) -[:JUDGED_IN]-> (c2)";
        session.run(sql, params);
    }

    public static void constructLawTableGraph(Session session, List<Map<String, Object>> props){
        Map<String, Object> params = new HashMap<>();
        params.put("props", props);
        String sql = " UNWIND $props AS prop " +
                "MERGE (law1: Law{name: prop.name}) " +
                "ON CREATE SET law1.content = prop.content, law1.belongs_law = prop.belongs_law, law1.level = prop.level " +
                "WITH law1 " +
                "MATCH (law2: Law{name: law1.belongs_law}) " +
                "MERGE (law1) -[:BELONGS]-> (law2) " ;
        session.run(sql, params);
    }

    public static void constructLawClauseGraph(Session session, List<Map<String, Object>> props) {
        Map<String, Object> params = new HashMap<>();
        params.put("props", props);
        String sql = " UNWIND $props AS prop " +
                "MERGE (law1: Law{name: prop.name}) " +
                "ON CREATE SET law1.content = prop.content, " +
                " law1.belongs_law = prop.belongs_law, law1.level = prop.level, " +
                " law1.publish_date = prop.publish_date, law1.implement_date = prop.implement_date " +
                "ON MATCH SET law1.content = prop.content, " +
                " law1.belongs_law = prop.belongs_law, law1.level = prop.level, " +
                " law1.publish_date = prop.publish_date, law1.implement_date = prop.implement_date " +
                "WITH law1, prop " +
                "MATCH (law2:Law) WHERE law2.name =~ prop.matchStr " +
                "WITH law1, law2 " +
                "MERGE (law1) -[:BELONGS]-> (law2) ";
        session.run(sql, params);
    }

    public static void addJudgementContent(Session session, List<Map<String, Object>> props) {
        String sql = " CALL apoc.periodic.iterate( " +
                "'UNWIND $node_props as props  RETURN  props',  " +
                "'MERGE (c:Content{doc_id: props.doc_id}) SET c = props WITH props.doc_id as doc_id" +
                " MATCH (j:Judgement{doc_id: doc_id}), (c:Content{doc_id: doc_id}) MERGE (j) -[:CONTAINS]-> (c)', " +
                "{ batchSize:1000, iterateList:true,  params: {node_props:$node_props} } " +
                ")";
        session.run(sql, ImmutableMap.of("node_props", props));

    }

}
